import { findParent } from './utils.js';
const dpr = window.devicePixelRatio || 1;

export default class XMind {
    constructor(el, options = {}) {
        if (!el) throw new Error('el is required');
        this.el = typeof el === 'string' ? document.querySelector(el) : el;
        this.options = options;

        this.nodes = [];
        this.selectedNodes = [];
        this.draggingNode = null;
        this.draggingCanvas = false;
        this.offsetX = 0;
        this.offsetY = 0;
        this.translateX = 0;
        this.translateY = 0;
        this.scale = 1;
        this.contextMenu = null;

        this.init();
        this.bindEvents();
    }

    init() {
        let layoutWidth, layoutHeight;
        if (!this.options.width) {
            const elParent = findParent(this.el, 'canvas');
            const rect = elParent.getBoundingClientRect();
            layoutWidth = rect.width;
            layoutHeight = rect.height;
        } else {
            layoutWidth = this.options.width;
            layoutHeight = this.options.height;
        }

        this.el.width = layoutWidth * dpr;
        this.el.height = layoutHeight * dpr;
        this.el.style.width = layoutWidth + 'px';
        this.el.style.height = layoutHeight + 'px';

        this.bgColor = this.options.bgColor || 'white';
        this.width = this.el.width;
        this.height = this.el.height;

        this.ctx = this.el.getContext('2d');
        this.redraw();
    }

    bindEvents() {
        this.el.addEventListener('mousedown', this.onMouseDown.bind(this));
        this.el.addEventListener('mousemove', this.onMouseMove.bind(this));
        this.el.addEventListener('mouseup', this.onMouseUp.bind(this));
        this.el.addEventListener('mouseleave', this.onMouseUp.bind(this));
        this.el.addEventListener('dblclick', this.onDblClick.bind(this));
        this.el.addEventListener('contextmenu', this.onContextMenu.bind(this));
        this.el.addEventListener('wheel', this.onWheel.bind(this));
        window.addEventListener('keydown', this.onKeyDown.bind(this));
    }

    getMousePos(e) {
        const rect = this.el.getBoundingClientRect();
        const x = (e.clientX - rect.left - this.translateX) / this.scale;
        const y = (e.clientY - rect.top - this.translateY) / this.scale;
        return { x, y };
    }

    redraw() {
        const ctx = this.ctx;
        ctx.save();
        ctx.clearRect(0, 0, this.width, this.height);

        ctx.translate(this.translateX, this.translateY);
        ctx.scale(this.scale, this.scale);

        ctx.fillStyle = this.bgColor;
        ctx.fillRect(-this.translateX / this.scale, -this.translateY / this.scale, this.width / this.scale, this.height / this.scale);

        this.drawAllConnections();

        this.nodes.forEach(node => this.drawNode(node));

        ctx.restore();
    }

    drawAllConnections() {
        const groups = {};
        for (const node of this.nodes) {
            if (node.parentId) {
                if (!groups[node.parentId]) groups[node.parentId] = [];
                groups[node.parentId].push(node);
            }
        }

        for (const parentId in groups) {
            const children = groups[parentId];
            const parent = this.nodes.find(n => n.id === parentId);
            if (!parent) continue;

            const parentX = parent.x + parent.width / 2;
            const parentY = parent.y + parent.height / 2;

            for (const child of children) {
                const childX = child.x + child.width / 2;
                const childY = child.y + child.height / 2;

                if (children.length === 1) {
                    this.drawLine(parentX, parentY, childX, childY, '#999', 1);
                } else {
                    this.drawSmoothCurve(parentX, parentY, childX, childY, '#999', 1);
                }
            }
        }
    }

    drawLine(fromX, fromY, toX, toY, color = 'black', lineWidth = 1) {
        const ctx = this.ctx;
        const d = dpr;
        ctx.beginPath();
        ctx.moveTo(fromX * d, fromY * d);
        ctx.lineTo(toX * d, toY * d);
        ctx.strokeStyle = color;
        ctx.lineWidth = lineWidth * d;
        ctx.stroke();
    }

    // 优化的贝塞尔曲线连线
    drawSmoothCurve(fromX, fromY, toX, toY, color = 'black', lineWidth = 1) {
        const ctx = this.ctx;
        const d = dpr;
        ctx.beginPath();

        const dx = toX - fromX;
        const dy = toY - fromY;

        // 控制点偏移，X方向偏移较大，Y方向根据两点高度差调整
        const cp1x = fromX + dx * 0.3;
        const cp1y = fromY;

        const cp2x = fromX + dx * 0.7;
        const cp2y = toY;

        ctx.moveTo(fromX * d, fromY * d);
        ctx.bezierCurveTo(cp1x * d, cp1y * d, cp2x * d, cp2y * d, toX * d, toY * d);

        ctx.strokeStyle = color;
        ctx.lineWidth = lineWidth * d;
        ctx.stroke();
    }

    drawNode(node) {
        const ctx = this.ctx;

        // 先测量文本高度
        const maxWidth = 400;
        const padding = 10;
        const maxLines = 3;

        const { lines, lineHeight, height } = this.measureTextLines(node.text, maxWidth, node.fontSize || 14, maxLines);

        // 根据文本高度更新节点高度（最小60）
        node.height = Math.max(height + padding * 2, 60);
        node.width = Math.max(node.width || 100, maxWidth + padding * 2);

        // 背景色
        let fillColor = node.fillColor || '#fff';
        if (this.selectedNodes.includes(node.id)) fillColor = '#ffd';
        else if (node.id === this.hoverNodeId) fillColor = '#eef';

        const sx = node.x;
        const sy = node.y;
        const sWidth = node.width;
        const sHeight = node.height;
        const sRadius = node.radius || 5;

        // 绘制圆角矩形背景
        ctx.beginPath();
        ctx.moveTo(sx + sRadius, sy);
        ctx.lineTo(sx + sWidth - sRadius, sy);
        ctx.quadraticCurveTo(sx + sWidth, sy, sx + sWidth, sy + sRadius);
        ctx.lineTo(sx + sWidth, sy + sHeight - sRadius);
        ctx.quadraticCurveTo(sx + sWidth, sy + sHeight, sx + sWidth - sRadius, sy + sHeight);
        ctx.lineTo(sx + sRadius, sy + sHeight);
        ctx.quadraticCurveTo(sx, sy + sHeight, sx, sy + sHeight - sRadius);
        ctx.lineTo(sx, sy + sRadius);
        ctx.quadraticCurveTo(sx, sy, sx + sRadius, sy);
        ctx.closePath();

        ctx.fillStyle = fillColor;
        ctx.fill();

        ctx.lineWidth = node.lineWidth || 1;
        ctx.strokeStyle = node.strokeColor || '#333';
        ctx.stroke();

        // 绘制多行文本，顶边距开始绘制
        ctx.fillStyle = node.textColor || '#333';
        ctx.font = `${node.fontSize || 14}px Arial`;
        ctx.textBaseline = 'top';
        ctx.textAlign = 'center';

        let curY = sy + padding;
        const centerX = sx + sWidth / 2;

        for (const lineText of lines) {
            ctx.fillText(lineText, centerX, curY);
            curY += lineHeight;
        }
    }

    drawTextWithWrap(text, x, y, maxWidth, fontSize, color, maxLines = 3, maxHeight) {
        const ctx = this.ctx;
        ctx.fillStyle = color;
        ctx.font = `${fontSize}px Arial`;
        ctx.textBaseline = 'top';

        const words = text.split('');
        let line = '';
        let lineCount = 0;
        let lineHeight = fontSize * 1.2;
        let curY = y;

        for (let i = 0; i < words.length; i++) {
            const testLine = line + words[i];
            const metrics = ctx.measureText(testLine);
            if (metrics.width > maxWidth && line.length > 0) {
                lineCount++;
                if (lineCount === maxLines) {
                    // 处理省略号
                    let trimmedLine = line;
                    while (ctx.measureText(trimmedLine + '...').width > maxWidth) {
                        trimmedLine = trimmedLine.slice(0, -1);
                    }
                    ctx.fillText(trimmedLine + '...', x, curY);
                    return;
                } else {
                    ctx.fillText(line, x, curY);
                    line = words[i];
                    curY += lineHeight;
                    if (curY + lineHeight - y > maxHeight) break; // 超高停止绘制
                }
            } else {
                line = testLine;
            }
        }
        if (lineCount < maxLines) {
            ctx.fillText(line, x, curY);
        }
    }

    addNode(node) {
        this.nodes.push(node);
        this.redraw();
    }

    autoLayoutChildren(parentId) {
        const children = this.nodes.filter(n => n.parentId === parentId);
        if (!children.length) return;

        const parent = this.nodes.find(n => n.id === parentId);
        if (!parent) return;

        const startX = parent.x + 200;
        const startY = parent.y + parent.height / 2;

        const gapY = 60;
        const totalHeight = (children.length - 1) * gapY;
        const topY = startY - totalHeight / 2;

        children.forEach((child, i) => {
            child.x = startX;
            child.y = topY + i * gapY - child.height / 2;
        });
    }

    deleteNode(node) {
        const parentId = node.parentId;

        const deleteRecursive = (nodeId) => {
            this.nodes = this.nodes.filter(n => n.id !== nodeId);
            this.nodes.filter(n => n.parentId === nodeId).forEach(child => deleteRecursive(child.id));
        };
        deleteRecursive(node.id);

        if (parentId) this.autoLayoutChildren(parentId);
        this.redraw();
    }

    onMouseDown(e) {
        const { x, y } = this.getMousePos(e);

        for (const node of this.nodes) {
            if (
                x >= node.x &&
                x <= node.x + node.width &&
                y >= node.y &&
                y <= node.y + node.height
            ) {
                this.draggingNode = node;
                this.offsetX = x - node.x;
                this.offsetY = y - node.y;

                if (e.shiftKey) {
                    if (!this.selectedNodes.includes(node.id)) this.selectedNodes.push(node.id);
                } else {
                    this.selectedNodes = [node.id];
                }
                this.redraw();
                return;
            }
        }
        this.draggingCanvas = true;
        this.offsetX = e.clientX;
        this.offsetY = e.clientY;
        this.selectedNodes = [];
        this.redraw();
    }

    onMouseMove(e) {
        if (this.draggingNode) {
            const { x, y } = this.getMousePos(e);
            this.draggingNode.x = x - this.offsetX;
            this.draggingNode.y = y - this.offsetY;
            this.redraw();
        } else if (this.draggingCanvas) {
            const dx = e.clientX - this.offsetX;
            const dy = e.clientY - this.offsetY;
            this.translateX += dx;
            this.translateY += dy;
            this.offsetX = e.clientX;
            this.offsetY = e.clientY;
            this.redraw();
        }
    }

    onMouseUp() {
        this.draggingNode = null;
        this.draggingCanvas = false;
    }

    onDblClick(e) {
        const { x, y } = this.getMousePos(e);

        for (const node of this.nodes) {
            if (
                x >= node.x &&
                x <= node.x + node.width &&
                y >= node.y &&
                y <= node.y + node.height
            ) {
                this.showEditInput(node);
                e.preventDefault();
                return;
            }
        }
    }

    showEditInput(node) {
        const input = document.createElement('input');
        input.type = 'text';
        input.value = node.text;
        input.style.position = 'fixed';
        input.style.left = (node.x * this.scale + this.translateX) + 'px';
        input.style.top = (node.y * this.scale + this.translateY) + 'px';
        input.style.width = node.width * this.scale + 'px';
        input.style.height = node.height * this.scale + 'px';
        input.style.fontSize = (node.fontSize || 14) * this.scale + 'px';
        input.style.border = '1px solid #ccc';
        input.style.zIndex = 10000;
        document.body.appendChild(input);
        input.focus();

        const save = () => {
            node.text = input.value;
            document.body.removeChild(input);
            this.redraw();
        };

        input.addEventListener('blur', save);
        input.addEventListener('keydown', e => {
            if (e.key === 'Enter') save();
        });
    }

    onContextMenu(e) {
        e.preventDefault();
        const { x, y } = this.getMousePos(e);

        let targetNode = null;
        for (const node of this.nodes) {
            if (
                x >= node.x &&
                x <= node.x + node.width &&
                y >= node.y &&
                y <= node.y + node.height
            ) {
                targetNode = node;
                break;
            }
        }

        if (targetNode) {
            this.showContextMenu(e.clientX, e.clientY, targetNode);
        } else {
            this.hideContextMenu();
        }
    }

    showContextMenu(pageX, pageY, node) {
        this.hideContextMenu();

        const menu = document.createElement('div');
        menu.style.position = 'fixed';
        menu.style.left = `${pageX}px`;
        menu.style.top = `${pageY}px`;
        menu.style.background = '#fff';
        menu.style.border = '1px solid #ccc';
        menu.style.boxShadow = '0 2px 6px rgba(0,0,0,0.2)';
        menu.style.zIndex = 10000;
        menu.style.userSelect = 'none';

        const items = [
            { label: '新增子节点', action: () => this.addChildNode(node) },
            { label: '编辑文本', action: () => this.showEditInput(node) },
            { label: '删除节点', action: () => this.deleteNode(node) }
        ];

        items.forEach(item => {
            const li = document.createElement('div');
            li.innerText = item.label;
            li.style.padding = '5px 15px';
            li.style.cursor = 'pointer';
            li.addEventListener('click', () => {
                item.action();
                this.hideContextMenu();
            });
            li.addEventListener('mouseenter', () => li.style.background = '#f0f0f0');
            li.addEventListener('mouseleave', () => li.style.background = '#fff');
            menu.appendChild(li);
        });

        document.body.appendChild(menu);
        this.contextMenu = menu;

        const hideOnClickOutside = (ev) => {
            if (!menu.contains(ev.target)) {
                this.hideContextMenu();
                document.removeEventListener('click', hideOnClickOutside);
            }
        };
        setTimeout(() => {
            document.addEventListener('click', hideOnClickOutside);
        }, 0);
    }

    hideContextMenu() {
        if (this.contextMenu) {
            document.body.removeChild(this.contextMenu);
            this.contextMenu = null;
        }
    }
    // 文字测量，返回行数组和行高
    measureTextLines(text, maxWidth, fontSize, maxLines = 3) {
        const ctx = this.ctx;
        ctx.font = `${fontSize}px Arial`;

        const words = text.split('');
        let lines = [];
        let line = '';

        for (let i = 0; i < words.length; i++) {
            const testLine = line + words[i];
            const metrics = ctx.measureText(testLine);
            if (metrics.width > maxWidth && line.length > 0) {
                lines.push(line);
                line = words[i];
                if (lines.length === maxLines) break;
            } else {
                line = testLine;
            }
        }
        if (lines.length < maxLines && line) lines.push(line);

        // 省略号处理
        if (lines.length > maxLines) {
            let lastLine = lines[maxLines - 1];
            while (ctx.measureText(lastLine + '...').width > maxWidth) {
                lastLine = lastLine.slice(0, -1);
            }
            lines[maxLines - 1] = lastLine + '...';
            lines = lines.slice(0, maxLines);
        }

        const lineHeight = fontSize * 1.2;

        return {
            lines,
            lineHeight,
            height: lineHeight * lines.length
        };
    }
    addChildNode(parent) {
        const newNode = {
            id: 'node_' + Date.now(),
            text: '新节点',
            x: parent.x + 150,
            y: parent.y + 50,
            width: 100,
            height: 30,
            radius: 8,
            fillColor: '#fff',
            strokeColor: '#333',
            lineWidth: 1,
            fontSize: 14,
            parentId: parent.id
        };
        this.addNode(newNode);
        this.autoLayoutChildren(parent.id);
        this.redraw();
    }

    onWheel(e) {
        e.preventDefault();

        const oldScale = this.scale;
        const delta = e.deltaY > 0 ? -0.1 : 0.1;
        this.scale = Math.min(3, Math.max(0.2, this.scale + delta));

        const rect = this.el.getBoundingClientRect();
        const mouseX = e.clientX - rect.left;
        const mouseY = e.clientY - rect.top;

        const dx = mouseX - this.translateX;
        const dy = mouseY - this.translateY;

        this.translateX -= dx * (this.scale - oldScale) / this.scale;
        this.translateY -= dy * (this.scale - oldScale) / this.scale;

        this.redraw();
    }

    onKeyDown(e) {
        if (e.key === 'Delete' || e.key === 'Backspace') {
            this.selectedNodes.forEach(id => {
                const node = this.nodes.find(n => n.id === id);
                if (node) this.deleteNode(node);
            });
            this.selectedNodes = [];
        }
    }
}
